package com.ldl.handle.spring.framework.webmvc.servlet;

import com.ldl.handle.spring.framework.annotation.LdlController;
import com.ldl.handle.spring.framework.annotation.LdlRequestMapping;
import com.ldl.handle.spring.framework.annotation.LdlRequestParam;
import com.ldl.handle.spring.framework.context.LdlApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author ldl  想过夏天的雪人
 * @Date 2022-10-13 13:47
 */
public class LdlDispatcherServlet extends HttpServlet {
    /*    //配置文件信息
        private Properties contentConfig = new Properties();
        //扫描问类路径名称
        private List<String> classNames = new ArrayList<String>();*/
    //ioc容器bean
//    private Map<String, Object> ioc = new HashMap<String, Object>();
    //路由对应的访问方法
    private Map<String, Method> handlerMapping = new HashMap<String, Method>();

    private LdlApplicationContext applicationContext;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //6.根据url委派给具体的调用方法
        try {
            resp.setCharacterEncoding("GBK");
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception,Detail: " + Arrays.toString(e.getStackTrace()));
        }

        System.out.println("LDL Spring framework is init");

    }


    @Override
    public void init(ServletConfig config) throws ServletException {
        //1.加载配置文件
        this.applicationContext = new LdlApplicationContext(config.getInitParameter("contextConfigLocation"));

        doInitHandlerMapping();
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath, "".replaceAll("/++", "/"));
        if (!this.handlerMapping.containsKey(url)) {
            resp.getWriter().write("404 Not Found!!!");
            return;
        }
        Method method = handlerMapping.get(url);
        //获取参数
        Map<String, String[]> params = req.getParameterMap();
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        //处理参数
        //1 参数位置和名称简历关系，缓存
        Map<String, Integer> paramIndexMapping = new HashMap<String, Integer>();
        //获取注解参数注解的key
        Annotation[][] pa = method.getParameterAnnotations();
        for (int i = 0; i < pa.length; i++) {
            for (Annotation a : pa[i]) {
                if (a instanceof LdlRequestParam) {
                    String paramName = ((LdlRequestParam) a).value();
                    if (!"".equals(paramName.trim())) {
                        paramIndexMapping.put(paramName, i);
                    }
                }
            }
        }
        Class<?>[] paramTypes = method.getParameterTypes();
        for (int i = 0; i < paramTypes.length; i++) {
            Class<?> type = paramTypes[i];
            if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                paramIndexMapping.put(type.getName(), i);
            }
        }

        Object[] paramValues = new Object[paramTypes.length];
//        Map<String,String[]> params=req.getParameterMap();
        for (Map.Entry<String, String[]> param : params.entrySet()) {
            String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll("\\s", "");
            if (!paramIndexMapping.containsKey(param.getKey())) {
                continue;
            }
            int index = paramIndexMapping.get(param.getKey());
            //todo 存在类型转换问题
            paramValues[index] = value;
        }
        if (paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
            int index = paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[index] = req;
        }
        if (paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
            int index = paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[index] = resp;
        }
        method.invoke(applicationContext.getBean(beanName), paramValues);
    }

    private void doInitHandlerMapping() {
        if (this.applicationContext.getBeanDefinitionCount() == 0) {
            return;
        }

        for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
            Object instance = applicationContext.getBean(beanName);

            Class<?> clazz = instance.getClass();
            if (!clazz.isAnnotationPresent(LdlController.class)) {
                continue;
            }
            //判断class 有没有路由
            String baseUrl = "";
            if (clazz.isAnnotationPresent(LdlRequestMapping.class)) {
                LdlRequestMapping requestMapping = clazz.getAnnotation(LdlRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            for (Method method : clazz.getDeclaredMethods()) {
                if (!method.isAnnotationPresent(LdlRequestMapping.class)) {
                    continue;
                }

                LdlRequestMapping requestMapping = method.getAnnotation(LdlRequestMapping.class);
                //会出现斜杠问题 有斜杠，无/
                String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
                handlerMapping.put(url, method);
                System.out.println("Mapped : " + url + " --> " + method);
            }
        }
    }


    private String toLowerFirstCase(String className) {
        char[] chars = className.toCharArray();
        chars[0] += 32; //ASCII码 大写字母和小写字母相差32 。。 类名小写字母开头的直接砍了
        return String.valueOf(chars);
    }

}
